From: Yehuda Katz + Carl Lerche Date: Tue, 10 Jun 2014 00:51:53 +0000 (-0700) Subject: Add support for detailed manifest dependencies X-Git-Tag: archive/raspbian/0.35.0-2+rpi1~3^2^2^2^2^2^2^2~1027 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/success//%22http:/www.example.com/cgi/success/?a=commitdiff_plain;h=92602f6db061e5d1611866539222a9aeeaf0a949;p=cargo.git Add support for detailed manifest dependencies This commit supports the following format: ```toml [dependencies.hamcrest] version = "1.0" git = "http://github.com/carllerche/hamcrest-rust" ``` --- diff --git a/MANIFEST.md b/MANIFEST.md index e331a629b..9037b6503 100644 --- a/MANIFEST.md +++ b/MANIFEST.md @@ -23,14 +23,14 @@ into Rust structs that are used throughout the built-in commands. ## The `[project]` Section -* `name`: the name of the project (`~str`) -* `version`: the version of the project, (`~str` that can be parsed +* `name`: the name of the project (`String`) +* `version`: the version of the project, (`String` that can be parsed via `semver::parse`) * `readme`: a Markdown-formatted file in the project that can be used as a description of the document in indexes (`Option`, relative to the project root, defaults to "./README.md", if found). -* `tags`: an array of tags that can be used in indexes (`~[~str]`) -* `authors`: a list of authors in `name ` format (`~[~str]`). At +* `tags`: an array of tags that can be used in indexes (`Vec`) +* `authors`: a list of authors in `name ` format (`Vec`). At least one `author` with email will probably be required to submit to the Cargo repository. * `src`: the root directory containing source files (`Option`, @@ -45,7 +45,7 @@ We only plan to support a single lib at the moment because if you have multiple libs, you would want projects to be able to depend on them separately. If you don't care about that, why do you have separate libs? -* `name`: the name of the library (`~str`, `hammer` would create a `libhammer`) +* `name`: the name of the library (`String`, `hammer` would create a `libhammer`) * `path`: the location of the main crate file (`Option`, defaults to `src/.rs`) @@ -64,13 +64,27 @@ main library, it should be shipped as a separate package with a dependency on the main library to keep the usage requirements of the standalone library limited to the bare minimum requirements. -* `name`: the name of the executable (`~str`, `hammer` would create a +* `name`: the name of the executable (`String`, `hammer` would create a `hammer` executable) * `path`: the location of the main crate file for the executable (`Option`, defaults to `src/.rs` if the project has only an executable, `src/bin/.rs` if the project has both a lib and executable, see below) +## The `[dependencies]` Section + +```toml +[dependencies] + +rust-http = "1.x" +hammer = ["> 1.2", "< 1.3.5"] + +[dependencies.hamcrest] + +version = "1.2.x" +git = "http://github.com/carllerche/hamcrest" +``` + ## Projects Containing Both `lib` and `executable` Most projects will primarily produce either a library or an executable. diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index a2be98d7d..6a57ca343 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -2,7 +2,7 @@ use std::fmt; use std::fmt::{Show,Formatter}; use std::collections::HashMap; use semver::Version; -use serialize::{Encoder,Encodable}; +use serialize::{Encoder,Decoder,Encodable,Decodable}; use core::{ Dependency, NameVer, @@ -10,7 +10,9 @@ use core::{ Summary }; use core::dependency::SerializedDependency; -use util::CargoResult; +use util::{CargoResult,Require,toml_error,simple_human}; +use toml; +use toml::{Table, ParseError}; #[deriving(PartialEq,Clone)] pub struct Manifest { @@ -192,21 +194,75 @@ pub struct Project { * TODO: Make all struct fields private */ -#[deriving(Decodable,Encodable,PartialEq,Clone)] +#[deriving(Encodable,PartialEq,Clone,Show)] +pub enum TomlDependency { + SimpleDep(String), + DetailedDep(HashMap) +} + +#[deriving(Encodable,PartialEq,Clone)] pub struct TomlManifest { project: Box, - lib: Option<~[TomlLibTarget]>, - bin: Option<~[TomlBinTarget]>, - dependencies: Option>, + lib: Option>, + bin: Option>, + dependencies: Option>, } impl TomlManifest { + pub fn from_toml(root: toml::Value) -> CargoResult { + fn decode>(root: &toml::Value, path: &str) -> Result { + let root = match root.lookup(path) { + Some(val) => val, + None => return Err(toml::ParseError) + }; + toml::from_toml(root.clone()) + } + + let project = try!(decode(&root, "project").map_err(|e| toml_error("ZOMG", e))); + let lib = decode(&root, "lib").ok(); + let bin = decode(&root, "bin").ok(); + + let deps = root.lookup("dependencies"); + + let deps = match deps { + Some(deps) => { + let table = try!(deps.get_table().require(simple_human("dependencies must be a table"))).clone(); + + let mut deps: HashMap = HashMap::new(); + + for (k, v) in table.iter() { + match v { + &toml::String(ref string) => { deps.insert(k.clone(), SimpleDep(string.clone())); }, + &toml::Table(ref table) => { + let mut details = HashMap::::new(); + + for (k, v) in table.iter() { + let v = try!(v.get_str() + .require(simple_human("dependency values must be string"))); + + details.insert(k.clone(), v.clone()); + } + + deps.insert(k.clone(), DetailedDep(details)); + }, + _ => () + } + } + + Some(deps) + }, + None => None + }; + + Ok(TomlManifest { project: box project, lib: lib, bin: bin, dependencies: deps }) + } + pub fn to_package(&self, path: &str) -> CargoResult { // TODO: Convert hte argument to take a Path let path = Path::new(path); // Get targets - let targets = normalize(&self.lib, &self.bin); + let targets = normalize(self.lib.as_ref().map(|l| l.as_slice()), self.bin.as_ref().map(|b| b.as_slice())); if targets.is_empty() { debug!("manifest has no build targets; project={}", self.project); @@ -218,7 +274,13 @@ impl TomlManifest { match self.dependencies { Some(ref dependencies) => { for (n, v) in dependencies.iter() { - deps.push(try!(Dependency::parse(n.as_slice(), v.as_slice()))); + let version = match *v { + SimpleDep(ref string) => string, + DetailedDep(ref map) => try!(map.find_equiv(&"version") + .require(simple_human("dependencies must include a version"))) + }; + + deps.push(try!(Dependency::parse(n.as_slice(), version.as_slice()))) } } None => () @@ -248,7 +310,7 @@ struct TomlTarget { path: Option } -fn normalize(lib: &Option<~[TomlLibTarget]>, bin: &Option<~[TomlBinTarget]>) -> Vec { +fn normalize(lib: Option<&[TomlLibTarget]>, bin: Option<&[TomlBinTarget]>) -> Vec { log!(4, "normalizing toml targets; lib={}; bin={}", lib, bin); fn lib_targets(dst: &mut Vec, libs: &[TomlLibTarget]) { @@ -267,17 +329,17 @@ fn normalize(lib: &Option<~[TomlLibTarget]>, bin: &Option<~[TomlBinTarget]>) -> let mut ret = Vec::new(); match (lib, bin) { - (&Some(ref libs), &Some(ref bins)) => { + (Some(ref libs), Some(ref bins)) => { lib_targets(&mut ret, libs.as_slice()); bin_targets(&mut ret, bins.as_slice(), |bin| format!("src/bin/{}.rs", bin.name)); }, - (&Some(ref libs), &None) => { + (Some(ref libs), None) => { lib_targets(&mut ret, libs.as_slice()); }, - (&None, &Some(ref bins)) => { + (None, Some(ref bins)) => { bin_targets(&mut ret, bins.as_slice(), |bin| format!("src/{}.rs", bin.name)); }, - (&None, &None) => () + (None, None) => () } ret diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index 94aa0f278..a4e693ad4 100644 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -4,6 +4,7 @@ #![allow(deprecated_owned_vector)] #![feature(macro_rules,phase)] +extern crate debug; extern crate term; extern crate url; extern crate serialize; diff --git a/src/cargo/ops/cargo_read_manifest.rs b/src/cargo/ops/cargo_read_manifest.rs index ec57001bc..a4b197aaa 100644 --- a/src/cargo/ops/cargo_read_manifest.rs +++ b/src/cargo/ops/cargo_read_manifest.rs @@ -19,7 +19,7 @@ fn parse_from_file(path: &str) -> CargoResult { } fn load_toml(root: toml::Value) -> CargoResult { - from_toml::(root).map_err(to_cargo_err) + TomlManifest::from_toml(root) } fn to_cargo_err(err: toml::Error) -> CargoError { diff --git a/src/cargo/sources/git/source.rs b/src/cargo/sources/git/source.rs index f1c5db2cc..bc985e443 100644 --- a/src/cargo/sources/git/source.rs +++ b/src/cargo/sources/git/source.rs @@ -49,6 +49,7 @@ impl Source for GitSource { } fn get(&self, packages: &[NameVer]) -> CargoResult> { + // TODO: Support multiple manifests per repo let pkg = try!(read_manifest(&self.checkout_path)); if packages.iter().any(|nv| pkg.is_for_name_ver(nv)) { diff --git a/tests/support.rs b/tests/support.rs index 282eb0dd1..efc34259a 100644 --- a/tests/support.rs +++ b/tests/support.rs @@ -273,7 +273,7 @@ impl ham::Matcher for Execs { match res { Ok(out) => self.match_output(&out), Err(CargoError { kind: ProcessError(_, ref out), .. }) => self.match_output(out.get_ref()), - Err(_) => Err(format!("could not exec process {}", process)) + Err(e) => Err(format!("could not exec process {}: {}", process, e)) } } } diff --git a/tests/test_cargo_compile.rs b/tests/test_cargo_compile.rs index 15d3a659f..f212d8ef8 100644 --- a/tests/test_cargo_compile.rs +++ b/tests/test_cargo_compile.rs @@ -207,6 +207,79 @@ test!(cargo_compile_with_nested_deps { execs().with_stdout("test passed\n")); }) +test!(cargo_compile_with_nested_deps_longhand { + let mut p = project("foo"); + let bar = p.root().join("bar"); + let baz = p.root().join("baz"); + + p = p + .file(".cargo/config", format!(r#" + paths = ["{}", "{}"] + "#, bar.display(), baz.display()).as_slice()) + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + + version = "0.5.0" + + [[bin]] + + name = "foo" + "#) + .file("src/foo.rs", main_file(r#""{}", bar::gimme()"#, ["bar"]).as_slice()) + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + + version = "0.5.0" + + [[lib]] + + name = "bar" + "#) + .file("bar/src/bar.rs", r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#) + .file("baz/Cargo.toml", r#" + [project] + + name = "baz" + version = "0.5.0" + authors = ["wycats@example.com"] + + [[lib]] + + name = "baz" + "#) + .file("baz/src/baz.rs", r#" + pub fn gimme() -> String { + "test passed".to_str() + } + "#); + + assert_that(p.cargo_process("cargo-compile"), execs()); + + assert_that(&p.root().join("target/foo"), existing_file()); + + assert_that( + cargo::util::process("foo").extra_path(p.root().join("target")), + execs().with_stdout("test passed\n")); +}) + fn main_file(println: &str, deps: &[&str]) -> String { let mut buf = String::new();